<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>深度克隆/深复制/深拷贝</title>
  </head>
  <body>
    <script src="https://cdn.bootcss.com/lodash.js/4.17.15/lodash.min.js"></script>
    <!-- 
  实现深拷贝
      1). 大众乞丐版
          问题1: 函数属性会丢失
          问题2: 循环引用会出错
      2). 面试基础版本
          解决问题1: 函数属性还没丢失
      3). 面试加强版本
          解决问题2: 循环引用正常
      4). 面试加强版本2(优化遍历性能)
          数组: while | for | forEach() 优于 for-in | keys()&forEach() 
          对象: for-in 与 keys()&forEach() 差不多
  -->
    <!-- <script src="./node_modules/atguigu-utils/dist/atguigu-utils.js"></script> -->
    <script type="text/javascript">
      const aUtils = {
        // 1). ---------------------------------------大众乞丐版
        deepClone1(target) {
          if (target !== null && typeof target === "object") {
            return JSON.parse(JSON.stringify(target));
          } else {
            return target;
          }
        },
        // 2). ---------------------------------------面试基础版本
        deepClone2(target) {
          if (target !== null && typeof target === "object") {
            // 非函数的对象
            // 创建一个新的空容器
            const clone = Array.isArray(target) ? [] : {};
            // 遍历target中所有数据, 依次添加到新容器
            for (const key in target) {
              // key是对象的属性名或数组的下标
              if (target.hasOwnProperty(key)) {
                // 如果是容器自身的才需要处理
                clone[key] = aUtils.deepClone2(target[key]); // 对属性值进行克隆处理后保存
              }
            }
            return clone;
          } else {
            return target;
          }
        },
        // 3). ---------------------------------------面试加强版本
        deepClone3(target, map = new Map()) {
          if (target !== null && typeof target === "object") {
            // 非函数的对象
            // 从缓存中取出对应的拷贝对象,如果有了, 直接返回它
            let clone = map.get(target);
            if (clone) return clone;
            // 如果没有, 创建一个新拷贝空容器, 缓存起来
            clone = Array.isArray(target) ? [] : {};
            map.set(target, clone);
            // 遍历target中所有数据, 依次添加到新容器
            for (const key in target) {
              // key是对象的属性名或数组的下标
              if (target.hasOwnProperty(key)) {
                // 如果是容器自身的才需要处理
                clone[key] = aUtils.deepClone3(target[key], map); // 对属性值进行克隆处理后保存
              }
            }
            return clone;
          } else {
            return target;
          }
        },
        // 4). ---------------------------------------面试加强版本2(优化遍历性能)
        deepClone4(target, map = new Map()) {
          if (target !== null && typeof target === "object") {
            // 非函数的对象
            // 从缓存中取出对应的拷贝对象,如果有了, 直接返回它
            let clone = map.get(target);
            if (clone) return clone;
            // 如果没有, 创建一个新拷贝空容器, 缓存起来
            if (Array.isArray(target)) {
              clone = [];
              map.set(target, clone);
              // 遍历target数组中所有元素, 依次添加到新容器
              target.forEach((item, index) => {
                clone[index] = aUtils.deepClone4(item, map);
              });
            } else {
              clone = {};
              map.set(target, clone);
              // 遍历target对象中所有数据, 依次添加到新容器
              for (const key in target) {
                // key是对象的属性名或数组的下标
                if (target.hasOwnProperty(key)) {
                  // 如果是容器自身的才需要处理
                  clone[key] = aUtils.deepClone4(target[key], map); // 对属性值进行克隆处理后保存
                }
              }
            }
            return clone;
          } else {
            return target;
          }
        },
      };
    </script>
    <script>
      const obj1 = {
        a: 1,
        b: ["e", "f", "g"],
        c: { h: { i: 2 } },
        d: function () {},
      };
      obj1.b.push(obj1.c);
      obj1.c.j = obj1.b;
      //  const obj1Clone = _.cloneDeep(obj1)
      //  const obj1Clone = aUtils.deepClone1(obj1)
      //  const obj1Clone = aUtils.deepClone2(obj1)
      //  const obj1Clone = aUtils.deepClone3(obj1)
      const obj1Clone = aUtils.deepClone4(obj1);
      console.log(obj1Clone, obj1Clone.c === obj1.c, obj1Clone.d === obj1.d);
    </script>
  </body>
</html>
